package opn.srn.command.load.parser.delimited;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import opn.srn.command.load.parser.FieldMetaData;
import opn.srn.command.load.parser.GroupMetaData;
import opn.srn.command.load.parser.IParser;
import opn.srn.command.load.parser.RecordMetaData;
import opn.srn.command.load.parser.stream.IStream;
import opn.srn.command.load.parser.stream.StringStream;
import opn.srn.command.load.parser.util.ParameterUtils;
import opn.srn.command.load.parser.util.ParserUtil;
import opn.srn.common.IMessage;

public class DelimitedParser implements IParser {

	char delimiter;
	Character escapeChar = null;

	List<FieldMetaData> fieldMetaDatas;

	@Override
	public void initialize(List<FieldMetaData> fieldMetaDatas) {
		this.fieldMetaDatas = fieldMetaDatas;
	}

	@Override
	public void parse(IStream stream, IMessage message) throws Exception {
		for (FieldMetaData fieldMetaData : fieldMetaDatas) {
			if (fieldMetaData instanceof GroupMetaData) {
				GroupMetaData groupMetaData = (GroupMetaData) fieldMetaData;
				IParser parser = groupMetaData.getParser();
				if (parser == null) {
					continue;
				}

				IStream groupStream = stream;
				if (groupMetaData.isGlobalParseScope() == false && groupMetaData.getAdditionalMetaInfo() != null) {
					/*
					 * String value;
					 * 
					 * if (escapeChar != null) { value = stream.readTill(delimiter, escapeChar, true); // ..TODO
					 * Optional group } else { value = stream.readTill(delimiter, true); // ..TODO Optional group }
					 */
					DelimitedFieldMetaData delimitedFieldMetaData = (DelimitedFieldMetaData) groupMetaData
							.getAdditionalMetaInfo();
					String value;
					if (delimitedFieldMetaData.getEscapeChar() != null) {
						value = stream.readTill(delimitedFieldMetaData.getDelimiter(),
								delimitedFieldMetaData.getEscapeChar(), delimitedFieldMetaData.isMandatory());
					} else {
						value = stream.readTill(delimitedFieldMetaData.getDelimiter(),
								delimitedFieldMetaData.isMandatory());
					}
					groupStream = new StringStream(value);
				}
				parser.parse(groupStream, message);
			} else if (fieldMetaData instanceof DelimitedFieldMetaData) {
				DelimitedFieldMetaData delimitedFieldMetaData = (DelimitedFieldMetaData) fieldMetaData;

				String value;
				if (delimitedFieldMetaData.getEscapeChar() != null) {
					value = stream.readTill(delimitedFieldMetaData.getDelimiter(),
							delimitedFieldMetaData.getEscapeChar(), delimitedFieldMetaData.isMandatory());
				} else {
					value = stream
							.readTill(delimitedFieldMetaData.getDelimiter(), delimitedFieldMetaData.isMandatory());
				}
				message.setData(delimitedFieldMetaData.getName(), value);
			}

		}
	}

	public char getDelimiter() {
		return delimiter;
	}

	public void setDelimiter(char delimiter) {
		this.delimiter = delimiter;
	}

	public Character getEscapeChar() {
		return escapeChar;
	}

	public void setEscapeChar(Character escapeChar) {
		this.escapeChar = escapeChar;
	}

	@Override
	public void load(RecordMetaData parentMetaData) throws Exception {
		List<RecordMetaData> members = parentMetaData.getMembers();

		Map<String, String> parameters = parentMetaData.getParameters();
		String delimiterValue = parameters.get("delimiter");
		if (delimiterValue == null) {
			throw new Exception("delimiter attribute missing in field " + parentMetaData.getName());
		}

		// delimiterValue = delimiterValue.trim();
		if (delimiterValue.length() != 1) {
			throw new Exception("delimiter(" + delimiterValue + ") value is invalid in field "
					+ parentMetaData.getName() + "...delimiter should be one character...");
		}
		setDelimiter(delimiterValue.charAt(0));

		String escapeCharValue = parameters.get("escape");
		if (escapeCharValue != null) {
			escapeCharValue = escapeCharValue.trim();
			if (escapeCharValue.length() == 1) {
				setEscapeChar(escapeCharValue.charAt(0));
			} else {
				throw new Exception("escape char(" + escapeCharValue + ") value is invalid in field "
						+ parentMetaData.getName() + "...escape char should be one character...");
			}
		}

		fieldMetaDatas = new ArrayList<FieldMetaData>();
		if (members != null) {
			for (RecordMetaData metaData : members) {
				fieldMetaDatas.add(createFieldMetaData(metaData));
			}
		}
	}

	private FieldMetaData createFieldMetaData(RecordMetaData recordMetaData) throws Exception {
		Map<String, String> parameters = recordMetaData.getParameters();

		FieldMetaData fieldMetaData = null;
		List<RecordMetaData> members = recordMetaData.getMembers();
		if (members != null && members.size() > 0) {
			IParser parser = ParserUtil.createParser(recordMetaData);
			GroupMetaData groupMetaData = new GroupMetaData(recordMetaData.getName());
			groupMetaData.setParser(parser);

			boolean globalScope = (ParameterUtils.get(parameters, "scope", "global").equals("global")) ? true : false;
			if (globalScope == false) {
				groupMetaData.setAdditionalMetaInfo(createDelimitedFieldMetaData(recordMetaData, "local."));
			}
			groupMetaData.setGlobalParseScope(globalScope);

			fieldMetaData = groupMetaData;
		} else {
			fieldMetaData = createDelimitedFieldMetaData(recordMetaData, "");
		}
		return fieldMetaData;
	}

	private DelimitedFieldMetaData createDelimitedFieldMetaData(RecordMetaData recordMetaData, String prefix) throws Exception {
		Map<String, String> parameters = recordMetaData.getParameters();

		DelimitedFieldMetaData fieldMetaData = new DelimitedFieldMetaData(recordMetaData.getName());
		fieldMetaData.setDelimiter(delimiter);
		String delimiterValue = parameters.get(prefix+"delimiter");
		if (delimiterValue != null) {
			delimiterValue = delimiterValue.trim();
			if (delimiterValue.length() != 1) {
				throw new Exception("delimiter(" + delimiterValue + ") value is invalid in field "
						+ recordMetaData.getName() + "...delimiter should be one character...");
			}
			fieldMetaData.setDelimiter(delimiterValue.charAt(0));
		}

		fieldMetaData.setEscapeChar(escapeChar);
		String escapeCharValue = parameters.get(prefix+"escape");
		if (escapeCharValue != null) {
			escapeCharValue = escapeCharValue.trim();
			if (escapeCharValue.length() == 1) {
				fieldMetaData.setEscapeChar(escapeCharValue.charAt(0));
			} else {
				throw new Exception("escape char(" + escapeCharValue + ") value is invalid in field "
						+ recordMetaData.getName() + "...escape char should be one character...");
			}
		}

		String mandatoryValue = parameters.get(prefix+"mandatory");
		if (mandatoryValue == null || mandatoryValue.equals("false")) {
			fieldMetaData.setMandatory(false);
		} else {
			fieldMetaData.setMandatory(true);
		}

		return fieldMetaData;
	}

	@Override
	public List<FieldMetaData> getFieldMetaDatas() {
		return fieldMetaDatas;
	}

}
